package com.pig.openg1.render;

import android.content.Context;
import android.opengl.GLES20;
import android.opengl.GLSurfaceView;
import android.opengl.Matrix;
import android.os.SystemClock;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class GraphFiveRender implements GLSurfaceView.Renderer {

    private final String TAG = GraphFiveRender.class.getSimpleName();

    private Context mContext;

    private float[] mModelMatrix = new float[16];
    private float[] mViewMatrix = new float[16];
    private float[] mProjectionMatrix = new float[16];
    private float[] mMVPMatrix = new float[16];

    private FloatBuffer mCubePosition;
    private FloatBuffer mCubeColor;

    private int mMVPMatrixHandle;
    private int mPositionHandle;
    private int mColorHandle;

    private final int mBytePerFloat = 4;
    private final int mPositionDataSize = 3;
    private final int mColorDataSize = 4;

    private int mProgramHandle;
    private boolean isBlending = true;


    public void switchMode()
    {
        isBlending = ! isBlending;
        if(isBlending)
        {
            GLES20.glDisable(GLES20.GL_CULL_FACE);
            GLES20.glDisable(GLES20.GL_DEPTH_TEST);
            GLES20.glEnable(GLES20.GL_BLEND);
            GLES20.glBlendFunc(GLES20.GL_ONE,GLES20.GL_ONE);

        }
        else
        {
            GLES20.glEnable(GLES20.GL_CULL_FACE);
            GLES20.glEnable(GLES20.GL_DEPTH_TEST);
            GLES20.glDisable(GLES20.GL_BLEND);
        }
    }

    public GraphFiveRender(Context mContext)
    {
        this.mContext = mContext;
        float[] p1p = {-1.0f,1.0f,1.0f};
        float[] p2p = {1.0f,1.0f,1.0f};
        float[] p3p = {-1.0f,-1.0f,1.0f};
        float[] p4p = {1.0f,-1.0f,1.0f};
        float[] p5p = {-1.0f,1.0f,-1.0f};
        float[] p6p = {1.0f,1.0f,-1.0f};
        float[] p7p = {-1.0f,-1.0f,-1.0f};
        float[] p8p = {1.0f,-1.0f,-1.0f};
        float[] mCubePositionData = generateCubeData(p1p,p2p,p3p,p4p,p5p,p6p,p7p,p8p,p1p.length);

        float[] p1c = {1.0f,0.0f,0.0f,1.0f}; //red
        float[] p2c = {1.0f,0.0f,1.0f,1.0f}; //magenta
        float[] p3c = {0.0f,0.0f,0.0f,1.0f}; //black
        float[] p4c = {0.0f,0.0f,1.0f,1.0f}; // blue
        float[] p5c = {1.0f,1.0f,0.0f,1.0f}; // yellow
        float[] p6c = {1.0f,1.0f,1.0f,1.0f}; // white
        float[] p7c = {0.0f,1.0f,0.0f,1.0f}; // green
        float[] p8c = {0.0f,1.0f,1.0f,1.0f}; // cyan
        float[] mCubeColorData = generateCubeData(p1c,p2c,p3c,p4c,p5c,p6c,p7c,p8c,p1c.length);

        mCubePosition = ByteBuffer.allocateDirect(mCubePositionData.length * mBytePerFloat)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mCubePosition.put(mCubePositionData).position(0);

        mCubeColor = ByteBuffer.allocateDirect(mCubeColorData.length * mBytePerFloat)
                .order(ByteOrder.nativeOrder()).asFloatBuffer();
        mCubeColor.put(mCubeColorData).position(0);

    }


    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {

        GLES20.glClearColor(0.0f,0.0f,0.0f,0.0f);
        GLES20.glDisable(GLES20.GL_CULL_FACE);
        GLES20.glDisable(GLES20.GL_DEPTH_TEST);

        //颜色混合模式
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_ONE,GLES20.GL_ONE);
        Matrix.setLookAtM(mViewMatrix,0,0.0f,0.0f,-0.5f,0.0f,0.0f,-5.0f,
                0.0f,1.0f,0.0f);

        String mVertexSource = loadAssert("color_vertex_shader.glsl");
        String mFragmentSource = loadAssert("color_fragment_shader.glsl");
        int mVertexShader = loadShader(GLES20.GL_VERTEX_SHADER,mVertexSource);
        int mFragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER,mFragmentSource);
        mProgramHandle = GLES20.glCreateProgram();
        if( mProgramHandle == 0 )
            throw new RuntimeException("Error create program !");
        GLES20.glAttachShader(mProgramHandle,mVertexShader);
        GLES20.glAttachShader(mProgramHandle,mFragmentShader);
        GLES20.glBindAttribLocation(mProgramHandle,0,"a_Position");
        GLES20.glBindAttribLocation(mProgramHandle,1,"a_Color");
        GLES20.glLinkProgram(mProgramHandle);
        int[] mLinkStatus = new int[1];
        GLES20.glGetProgramiv(mProgramHandle,GLES20.GL_LINK_STATUS,mLinkStatus,0);
        if( mLinkStatus[0] == 0 )
        {
            Log.e(TAG, "创建程序失败： " + GLES20.glGetProgramInfoLog(mProgramHandle));
            GLES20.glDeleteProgram(mProgramHandle);
            mProgramHandle = 0 ;
        }


    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

        GLES20.glViewport(0,0,width,height);
        float ratio = (float)width/height;
        Matrix.frustumM(mProjectionMatrix,0,-ratio,ratio,-1,1,1,10);
    }

    @Override
    public void onDrawFrame(GL10 gl) {

        if(isBlending)
        {
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
        }
        else
        {
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
        }
        long time = SystemClock.uptimeMillis() %10000L;
        float angleDegree = (360.0f / 10000.0f) * time;
        GLES20.glUseProgram(mProgramHandle);
        mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgramHandle,"u_MVPMatrix");
        mPositionHandle = GLES20.glGetAttribLocation(mProgramHandle,"a_Position");
        mColorHandle = GLES20.glGetAttribLocation(mProgramHandle,"a_Color");
        
        //draw some cube
        Matrix.setIdentityM(mModelMatrix,0);
        Matrix.translateM(mModelMatrix,0,4.0f,0.0f,-7.0f);
        Matrix.rotateM(mModelMatrix,0,angleDegree,1.0f,0.0f,0.0f);
        
        drawCube();


    }

    private void drawCube() {

        mCubePosition.position(0);
        GLES20.glVertexAttribPointer(mPositionHandle,mPositionDataSize,GLES20.GL_FLOAT,false,0,
                mCubePosition);
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        mCubeColor.position(0);
        GLES20.glVertexAttribPointer(mColorHandle,mColorDataSize,GLES20.GL_FLOAT,false,0
        ,mCubeColor);
        GLES20.glEnableVertexAttribArray(mColorHandle);

        Matrix.multiplyMM(mMVPMatrix,0,mViewMatrix,0,mModelMatrix,0);
        Matrix.multiplyMM(mMVPMatrix,0,mProjectionMatrix,0,mMVPMatrix,0);

        GLES20.glUniformMatrix4fv(mMVPMatrixHandle,1,false,mMVPMatrix,0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES,0,36);

    }


    private float[] generateCubeData(float[] point1,float[] point2,float[] point3,float[] point4,float[] point5,float[] point6,float[] point7,float[] point8,int elementPerPoint)
    {
        int front = 0 ;
        int right = 1;
        int back = 2;
        int left = 3;
        int top = 4;
        int size = elementPerPoint * 6 * 6;
        float[] mCubeData = new float[size];
        for(int face = 0; face < 6 ;face ++)
        {
            float[] p1,p2,p3,p4;
            if(face == front)
            {
                p1 = point1; p2 = point2; p3 = point3;p4 = point4;
            }
            else if(face == right)
            {
                p1 = point2;p2 = point6;p3 = point4;p4 = point8;
            }
            else if(face == back )
            {
                p1 = point6;p2 = point5;p3 = point8;p4 = point7;
            }
            else if(face == left)
            {
                p1 = point5;p2 = point1;p3 = point7;p4 = point3;
            }
            else if(face == top)
            {
                p1 = point5; p2 = point6; p3 = point1; p4 = point2;
            }
            else
            {
                p1 = point8;p2 = point7;p3 = point4;p4 = point3;
            }
            int offset = face * elementPerPoint * 6;
            for(int i=0;i < elementPerPoint;i++){mCubeData[offset++] = p1[i];}
            for(int i=0;i<elementPerPoint;i++){mCubeData[offset ++] = p3[i];}
            for(int i=0;i<elementPerPoint;i++){mCubeData[offset ++] = p2[i];}
            for(int i=0;i<elementPerPoint;i++){mCubeData[offset ++] = p3[i];}
            for(int i =0;i<elementPerPoint;i++){mCubeData[offset++] = p4[i];}
            for(int i=0;i<elementPerPoint;i++){mCubeData[offset++] = p2[i];}
        }
        return mCubeData;
    }

    private int loadShader(int type,String source)
    {
        int shader = GLES20.glCreateShader(type);
        if(shader == 0 )
            throw new RuntimeException("Error create shader !");
        GLES20.glShaderSource(shader,source);
        GLES20.glCompileShader(shader);
        int[] status = new int[1];
        GLES20.glGetShaderiv(shader,GLES20.GL_COMPILE_STATUS,status,0);
        if( status[0] == GLES20.GL_FALSE)
        {
            Log.e(TAG, "加载片元文件失败：" + GLES20.glGetShaderInfoLog(shader));
            GLES20.glDeleteShader(shader);
            shader = 0;
        }
        return shader;
    }

    private String loadAssert(String path)
    {
        String response = null;
        try{
            InputStream in = mContext.getAssets().open(path);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            int lenght = 0;
            byte[] buffer = new byte[1024];
            lenght = in.read(buffer);
            while(lenght > 0 )
            {
                out.write(buffer,0,lenght);
                lenght = in.read(buffer);
            }
            response =  out.toString();
            response = response.replaceAll("\\r\\n","\n");
            in.close();
            out.close();
        }catch (Exception e)
        {
            Log.e(TAG, "加载文件失败： -> "+ path + e.getMessage());
            return response;
        }
        return response;
    }

}
